/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#pragma once

#include <concurrent_queue.h>
#include <journal/models/interfaces.h>
#include <journal/views/interfaces.h>
#include <journal/views/notebook.h>
#include <journal/common/settings_service.hpp>

namespace journal { 

namespace b = baja;
namespace a = baja::app;

class root : 
    public a::iinput_capture,
    public a::iinput_observer,
    public view::inotebook_observer,
    public a::irender_observer,
    public view::ishare_source,
    public view::icommand_observer,
    public view::iphoto_sink,
    public std::enable_shared_from_this<root>
{
public:
    root(const std::shared_ptr<a::iinput_relay>& inputRelay);
    ~root() { this->cleanup(); }
    static std::shared_ptr<root> create(const std::shared_ptr<a::iinput_relay>& inputRelay);

    void initialize();

    // irender_observer
    void onRender();
    void onResize(const a::resize_args& args);
    void onScreenChange(const a::screen_change_args& args);
    void onVisibilityChanged(const bool visible);

    // iinput_observer
    void onPointer(const a::pointer_args& args, void* context = nullptr);
    void onGesture(const a::gesture_args& args, void* context = nullptr);
    void onKey(const a::key_args& args, void* context = nullptr);

    // isuspendable
    void onSuspend(const std::shared_ptr<a::isuspension_deferral>& deferral);

    // iinput_capture
    void setCapture(const std::shared_ptr<a::iinput_observer>& observer) 
    { 
        _inputRelay.lock()->setCapture(std::static_pointer_cast<a::iinput_observer>(shared_from_this())); 
        _capture.set(observer);
    }

    void releaseCapture()
    { 
        _inputRelay.lock()->releaseCapture();
        _capture.release();
    }

    std::shared_ptr<iinput_observer> getCapture(void** context = nullptr)
    { 
        return _capture.get(); 
    }

    // iink_surface_observer
    void onStrokeAdded(const std::shared_ptr<model::iink_stroke_model>& newStroke);
    void iink_surface_observer::onDirty() { /* noop */ }

    // ipage_observer
    void onPhotoAdded(const std::shared_ptr<view::iphoto>& photo);
    void onPhotoRemoved(const std::shared_ptr<view::iphoto>& photo);
    void onThumbnailSaved(const std::shared_ptr<view::ipage>& page);

    // iphoto_observer
    void onPhotoMoved(
        const std::shared_ptr<view::iphoto>& photo,
        const b::float32& newScale,
        const m::point2d& newPosition,
        const b::float32& newAngle
        );
    void onPhotoSelected(
        const std::shared_ptr<view::iphoto>& photo
        );
    void onPhotoDeselected();
    void iphoto_observer::onDirty() { /* noop */ }

    // ishare_source
    void onShareImageAsync(view::copy_info& info, std::function<void(const std::shared_ptr<view::ishared_image>&)> f);
    void onCopyImageAsync(view::copy_info& info, std::function<void(const std::shared_ptr<view::ishared_image>&)> f);
    void onPasteImageAsync(const std::shared_ptr<view::ishared_image>& image, std::function<void()> f);

    // icommand_observer
    void onCommand(const view::command_id id, void* context = nullptr);

    // iphoto_sink
    void onPhotoFileSelected(const std::shared_ptr<b::storage::istorage_file>& file);
private:
    void initView();
    void cleanup();
    void cleanupView();
    void initTestPhoto();
    void updateTestPhoto();
    void updatePenDebugString(const a::pointer_args& args);
    void saveTileAsync(std::function<void()> f);

private:
    std::weak_ptr<a::iinput_relay> _inputRelay;
    b::capturable<a::iinput_observer> _capture;

    std::shared_ptr<model::inotebook_model> _notebookModel;
    std::shared_ptr<view::inotebook> _notebookView;

    b::int32 _inputObserverCookie;
    b::int32 _settingsObserverCookie;
    b::int32 _notebookObserverCookie;

    std::vector<b::wchar> _penDebugString;
    static const b::uint32 max_debug_string = 256;

    bool _isSuspending;
    std::shared_ptr<a::isuspension_deferral> _deferral;
};

} // namespace journal